////////////////////////////////////////////////////////////////////////////////
// FILE: oclUtil.h
// AUTHOR: Nick Leaf
// 
// This OpenCL utility header contains functions to simplify OpenCL calls. 
// Rather than calling an error checking function after every ocl function, 
// error checking is toggled by a define at the beginning of the file (on by 
// default). This simplifies the code and gives it a cleaner appearance. 
// 
// TO DO:
//  - Add a way to do a 'cleanup' callback and exit from the code. Right now
//    the error is only reported to cerr; sometimes exit is called.
////////////////////////////////////////////////////////////////////////////////

#ifndef OCLUTIL_H
#define OCLUTIL_H

#include <CL/cl.h>

#ifdef CL_GL_INTEROP
#include <GL/gl.h>
#include <CL/cl_gl.h>
#endif

#include <string>
#include <vector>


//defines to automatically send the file names and line numbers
#define cluGetPlatformIDs(a,b,c) \
        _cluGetPlatformIDs(__FILE__,__LINE__,a,b,c)
#define cluPrintPlatformVersion(a) \
        _cluPrintPlatformVersion(__FILE__,__LINE__,a)
#define cluGetDeviceIDs(a,b,c,d,e) \
        _cluGetDeviceIDs(__FILE__,__LINE__,a,b,c,d,e)
#define cluGetDeviceInfo(a,b,c,d,e) \
        _cluGetDeviceInfo(__FILE__,__LINE__,a,b,c,d,e)
#define cluGetContextInfo(a,b,c,d,e) \
        _cluGetContextInfo(__FILE__,__LINE__,a,b,c,d,e)
#define cluBuildProgram(a,b,c,d,e,f) \
        _cluBuildProgram(__FILE__,__LINE__,a,b,c,d,e,f)
#define cluSetKernelArg(a,b,c,d) \
        _cluSetKernelArg(__FILE__,__LINE__,a,b,c,d)
#define cluReleaseMemObject(a) \
        _cluReleaseMemObject(__FILE__,__LINE__,a)
#define cluReleaseKernel(a) \
        _cluReleaseKernel(__FILE__,__LINE__,a)
#define cluReleaseProgram(a) \
        _cluReleaseProgram(__FILE__,__LINE__,a)
#define cluReleaseCommandQueue(a) \
        _cluReleaseCommandQueue(__FILE__,__LINE__,a)
#define cluReleaseContext(a) \
        _cluReleaseContext(__FILE__,__LINE__,a)
#define cluEnqueueWriteBuffer(a,b,c,d,e,f,g,h,i) \
        _cluEnqueueWriteBuffer(__FILE__,__LINE__,a,b,c,d,e,f,g,h,i)
#define cluEnqueueReadBuffer(a,b,c,d,e,f,g,h,i) \
        _cluEnqueueReadBuffer(__FILE__,__LINE__,a,b,c,d,e,f,g,h,i)
#define cluEnqueueCopyBuffer(a,b,c,d,e,f,g,h,i) \
        _cluEnqueueCopyBuffer(__FILE__,__LINE__,a,b,c,d,e,f,g,h,i)
#define cluEnqueueNDRangeKernel(a,b,c,d,e,f,g,h,i) \
        _cluEnqueueNDRangeKernel(__FILE__,__LINE__,a,b,c,d,e,f,g,h,i)
#define cluWaitForEvents(a,b) \
        _cluWaitForEvents(__FILE__,__LINE__,a,b)
#define cluFinish(a) \
        _cluFinish(__FILE__,__LINE__,a)
#define cluEnqueueReadImage(a,b,c,d,e,f,g,h,i,j,k) \
        _cluEnqueueReadImage(__FILE__,__LINE__,a,b,c,d,e,f,g,h,i,j,k)
#define cluEnqueueWriteImage(a,b,c,d,e,f,g,h,i,j,k) \
        _cluEnqueueWriteImage(__FILE__,__LINE__,a,b,c,d,e,f,g,h,i,j,k)
#define cluGetMemObjectInfo(a,b,c,d,e) \
        _cluGetMemObjectInfo(__FILE__,__LINE__,a,b,c,d,e)

#define cluCreateContext(a,b,c,d,e) \
        _cluCreateContext(__FILE__,__LINE__,a,b,c,d,e)
#define cluCreateContextFromType(a,b,c,d) \
        _cluCreateContextFromType(__FILE__,__LINE__,a,b,c,d)
#define cluCreateCommandQueue(a,b,c) \
        _cluCreateCommandQueue(__FILE__,__LINE__,a,b,c)
#define cluCreateProgramWithSource(a,b,c,d) \
        _cluCreateProgramWithSource(__FILE__,__LINE__,a,b,c,d)
#define cluCreateKernel(a,b) \
        _cluCreateKernel(__FILE__,__LINE__,a,b)
#define cluCreateBuffer(a,b,c,d) \
        _cluCreateBuffer(__FILE__,__LINE__,a,b,c,d)
#define cluCreateImage2D(a,b,c,d,e,f,g) \
        _cluCreateImage2D(__FILE__,__LINE__,a,b,c,d,e,f,g)
#define cluCreateImage3D(a,b,c,d,e,f,g,h,i) \
        _cluCreateImage3D(__FILE__,__LINE__,a,b,c,d,e,f,g,h,i)

#ifdef CL_GL_INTEROP
#define cluCreateFromGLBuffer(a,b,c) \
        _cluCreateFromGLBuffer(__FILE__,__LINE__,a,b,c)
#define cluCreateFromGLTexture2D(a,b,c,d,e) \
        _cluCreateFromGLTexture2D(__FILE__,__LINE__,a,b,c,d,e)
#define cluCreateFromGLTexture3D(a,b,c,d,e) \
        _cluCreateFromGLTexture3D(__FILE__,__LINE__,a,b,c,d,e)
#define cluEnqueueAcquireGLObjects(a,b,c,d,e,f) \
        _cluEnqueueAcquireGLObjects(__FILE__,__LINE__,a,b,c,d,e,f)
#define cluEnqueueReleaseGLObjects(a,b,c,d,e,f) \
        _cluEnqueueReleaseGLObjects(__FILE__,__LINE__,a,b,c,d,e,f)
#endif


//error code as return value
void _cluGetPlatformIDs(const char *, int, cl_uint, cl_platform_id*, cl_uint*);
void _cluPrintPlatformVersion(const char *, int, cl_platform_id platid);
void _cluGetDeviceIDs(const char *, int, cl_platform_id, cl_device_type,
        cl_uint, cl_device_id *, cl_uint *);
void _cluGetDeviceInfo(const char *, int, cl_device_id, cl_device_info, size_t,
        void *, size_t);
void _cluGetContextInfo(const char *, int, cl_context, cl_context_info, size_t, 
        void *, size_t *);
bool _cluBuildProgram(const char *, int, cl_program, cl_uint, 
        const cl_device_id *, const char *, 
        void (*clcb)(cl_program, void *), void *);
void _cluSetKernelArg(const char *, int, cl_kernel, cl_uint, size_t, 
        const void *);
void _cluReleaseMemObject(const char *, int, cl_mem);
void _cluReleaseKernel(const char *, int, cl_kernel);
void _cluReleaseProgram(const char *, int, cl_program);
void _cluReleaseCommandQueue(const char *, int, cl_command_queue);
void _cluReleaseContext(const char *, int, cl_context);
void _cluEnqueueWriteBuffer(const char*, int, cl_command_queue, cl_mem, cl_bool,
        size_t, size_t, const void *, cl_uint, const cl_event *, cl_event *);
void _cluEnqueueReadBuffer(const char *, int, cl_command_queue, cl_mem, cl_bool,
        size_t, size_t, void *, cl_uint, const cl_event *, cl_event *);
void _cluEnqueueCopyBuffer(const char *, int, cl_command_queue, cl_mem, cl_mem,
        size_t, size_t, size_t, cl_uint, const cl_event *, cl_event *);
void _cluEnqueueNDRangeKernel(const char *, int, cl_command_queue, cl_kernel, 
        cl_uint, const size_t *, const size_t *, const size_t *, cl_uint,
        const cl_event *, cl_event *);
void _cluWaitForEvents(const char *, int, cl_uint, const cl_event *);
void _cluFinish(const char *, int, cl_command_queue);
void _cluEnqueueReadImage(const char *, int, cl_command_queue, cl_mem, cl_bool,
        const size_t *, const size_t *, size_t, size_t, void*,
        cl_uint, const cl_event*, cl_event*);
void _cluEnqueueWriteImage(const char *, int, cl_command_queue, cl_mem, cl_bool,
        const size_t *, const size_t *, size_t, size_t, const void*,
        cl_uint, const cl_event*, cl_event*);
void _cluGetMemObjectInfo(const char *, int, cl_mem, cl_mem_info, size_t,
        void *, size_t *);


//pointer to error code as parameter
cl_context _cluCreateContext(const char *, int, cl_context_properties *,
        cl_uint, const cl_device_id *,
        void (*clcb)(const char*, const void*, size_t, void *), void *);
cl_context _cluCreateContextFromType(const char *, int, cl_context_properties *,
        cl_device_type, 
        void (*clcb)(const char *, const void *, size_t, void *), void *);
cl_command_queue _cluCreateCommandQueue(const char *, int, cl_context,
        cl_device_id, cl_command_queue_properties);
cl_program _cluCreateProgramWithSource(const char *, int, cl_context, cl_uint, 
        const char **, const size_t *);
cl_kernel _cluCreateKernel(const char *, int, cl_program, const char *);
cl_mem _cluCreateBuffer(const char *, int, cl_context, cl_mem_flags, 
        size_t, void *);
cl_mem _cluCreateImage2D(const char *, int, cl_context, cl_mem_flags,
        const cl_image_format *, size_t, size_t, size_t, void *);
cl_mem _cluCreateImage3D(const char *, int, cl_context, cl_mem_flags,
        const cl_image_format *, size_t, size_t, size_t, size_t, size_t,
        void *);
#ifdef CL_GL_INTEROP
cl_mem _cluCreateFromGLBuffer(const char *, int, cl_context, cl_mem_flags, 
        GLuint);
cl_mem _cluCreateFromGLTexture2D(const char *, int, cl_context, cl_mem_flags,
        GLenum, GLint, GLuint);
cl_mem _cluCreateFromGLTexture3D(const char *, int, cl_context, cl_mem_flags,
        GLenum, GLint, GLuint);

void _cluEnqueueAcquireGLObjects(const char *, int, cl_command_queue,
      cl_uint, const cl_mem *, cl_uint, const cl_event *, cl_event *);
void _cluEnqueueReleaseGLObjects(const char *, int, cl_command_queue,
      cl_uint, const cl_mem *, cl_uint, const cl_event *, cl_event *);
#endif


//utility
void cluCheckProgBuildLog(cl_program prog, cl_device_id did);
char *cluLoadProg(const char *filename);
void cluParseErr(cl_int err);
bool cluSupportsExtensions(cl_device_id dev, std::vector<std::string> extStrs);
bool cluSupportsExtension(cl_device_id dev, std::string extStr);

#endif
